package guet.imagan.progbuilder;

import guet.imagan.util.FileUtil;
import guet.imagan.util.ListUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 表示1个软件项目
 */
public abstract class Project {
	/**
	 * 项目中所有源文件,注意，第1个源文件应该为主模块。对于C语言，第1个文件为主函数所在源文件
	 * ，对于Java语言，第1个文件为主类源文件，对于Python而言，启动程序的文件主模块文件。
	 */
	private final List<File> srcFiles=new ArrayList<>();
	/**
	 * 项目编译构建时的输出路径，默认值为项目根目录下的的build_output
	 */
	private File outputDir;

	//源文件扩展名，例如"java"
	private final String srcFileExt;
	//项目根目录
	private File path;

	/**
	 * 根据项目根目录及源文件扩展名创建Project对象
	 *
	 * @param path       项目根目录
	 * @param srcFileExt 源文件扩展名
	 */
	public Project(File path, String srcFileExt) throws ProjectException {
		this.path = path;
		this.srcFileExt = srcFileExt;
		updateFiles(path, srcFileExt);
		this.outputDir = new File(path.getAbsolutePath()+File.separator+
				"build_output");
	}

	/**
	 * 返回当前编程项目的源文件的扩展名
	 */
	public String getSrcFileExt() {
		return srcFileExt;
	}

	/**
	 * 根据当前路径和扩展名，更新源文件列表
	 */
	private void updateFiles(File path, String srcFileExt) throws ProjectException {
		FileUtil.findFiles(srcFiles,path
				, Collections.singletonList(srcFileExt));
		if(srcFiles.size()==0){
			throw new ProjectException("the project without any source files");
		}
	}

	/**
	 * 根据源文件列表创建Project对象
	 *
	 * @param srcFiles 源文件列表
	 */
	public Project(List<File> srcFiles) throws ProjectException {

		int len = 100000;
		for (File f : srcFiles) {
			this.srcFiles.add(f);
			String currentPath = f.getParent();
			int curLen = currentPath.length();
			if (curLen < len) {
				len = currentPath.length();
				this.path = f;
			}
		}
		this.srcFileExt = FileUtil.getFileExtension(srcFiles.get(0));
		this.outputDir = new File(path.getAbsolutePath()+File.separator+
				"build_output");
		if(srcFiles.size()==0) throw new ProjectException("the project have noting with the sourcefiles");
	}


	/**
	 * 返回项目根目录
	 */
	public File getPath() {
		return path;
	}

	/**
	 * 返回项目中所有源文件列表
	 */
	public List<File> getSrcFiles() {
		return this.srcFiles;
	}

	/**
	 * 返回项目的主模块名，或启动名，这个名称对于混合编程语言项目和纯解释型语言编程项目的运行来说，是必要的。
	 * @return
	 */
	public abstract String resolveMain();

	/**
	 * 返回项目的构建输出目录，通常是项目根目录下的"build_output"子目录
	 * 只有当调用这个方法时，才会检查该目录是否存在，不存在，则自动创建
	 * 该目录。
	 */
	public File getOutputDir() {
		if(!outputDir.exists())
			outputDir.mkdir();
		return outputDir;
	}

	/**
	 * 修改项目输出目录,如果希望客户能够修改项目输出目录，请在子类中将该方法的权限改为public
	 * @param outputDir 项目输出目录
	 */
	protected void setOutputDir(File outputDir){
		this.outputDir = outputDir;
	}

	/**
	 * 返回项目中的启动文件（主模块文件）列表。
	 * 对于C语言，就是主函数文件，对于Java语言，就是主类文件，对于python就是主模块
	 * 若项目只包含一个源文件，那这个源文件就是主文件了。
	 */
	public List<File> getMainFiles() {
		List<File> fs =new ArrayList<>();
		if(srcFiles.size() ==1)
			fs.add(srcFiles.get(0));
		if(srcFiles.size()>1){
			for(File f:srcFiles){
				if(Project.isMainFile(f)){
					fs.add(f);
				}
			}
		}
		return fs;
	}

	/**
	 * 判断一个源文件是否包含main函数、main类等表示程序启动的文件
	 * @param f  待读写的文件
	 * @return 为Main文件，返回true
	 */
	private static boolean isMainFile(File f) {
		TextFile tf = new TextFile(f.getAbsolutePath());
		return ListUtil.elementsContains(tf.getContent()," main");
	}

	/**
	 * 返回项目的主文件，若项目只有唯一源文件，则返回该源文件
	 * @throws ProjectException
	 */
	public File getMainFile() throws ProjectException {
		List<File> fs = getMainFiles();
		if(fs.size()==0){
			return srcFiles.get(0);
		}
		return fs.get(0);
	}

	/**
	 * 执行项目对象方法时产生的异常类
	 */
	public class ProjectException extends Exception {
		public ProjectException(String msg){
			super(msg);
		}
	}
}
